#ifndef ASFINGER_PBRPC_CORE_RPC_PLUGIN_H_
#define ASFINGER_PBRPC_CORE_RPC_PLUGIN_H_

#include <vector>

#include "core/config.h"
#include "core/helpers.h"
#include "core/struct_interface.h"

namespace asfinger {
namespace pbrpc {

namespace pb = google::protobuf;
namespace pbc = pb::compiler;

/*!
 * 获取前导或尾部注释
 * @tparam DescriptorType protobuf描述类型
 * @param desc 描述类型
 * @param leading 是否为前导
 * @param prefix 前缀
 * @return 前导或尾部注释
 */
template<typename DescriptorType>
inline std::string GetCommentsHelper(const DescriptorType *desc, bool leading,
                                     const std::string &prefix) {
  return GetPrefixedComments(desc, leading, prefix);
}

/*!
 * protobuf 方法
 */
class ProtoBufMethod
    : public MethodInterface {
 public:
  ProtoBufMethod(const pb::MethodDescriptor *method)
      : method_(method) {}

  /*!
   * 获取前导注释
   * @param prefix 前缀
   * @return 前导注释
   */
  std::string GetLeadingComments(const std::string prefix) const override {
    return GetCommentsHelper(method_, true, prefix);
  }

  /*!
   * 获取尾部注释
   * @param prefix 前缀
   * @return 尾部注释
   */
  std::string GetTrailingComments(const std::string prefix) const override {
    return GetCommentsHelper(method_, false, prefix);
  }

  /*!
   * 获取方法名
   * @return 方法名
   */
  std::string GetName() const override {
    return method_->name();
  }

  /*!
   * 设置输入类型类名称
   * @return 输入类型类名称
   */
  std::string GetInputTypeClassName() const override {
    return ClassName(method_->input_type(), true);
  }

  /*!
   * 设置输出类型类名称
   * @return 输出类型类名称
   */
  std::string GetOutputTypeClassName() const override {
    return ClassName(method_->output_type(), true);
  }

  /*!
   * 获取输入类型文件名称
   * @return 输入类型文件名称
   */
  std::string GetInputTypeFileName() const override {
    return method_->input_type()->file()->name();
  }

  /*!
   * 获取输出类型文件名称
   * @return 输出类型文件名称
   */
  std::string GetOutputTypeFileName() const override {
    return method_->output_type()->file()->name();
  }

  /*!
   * 非缓冲流
   * @return true 非缓冲流
   * @return false 缓冲流
   */
  bool IsNoStreaming() const override {
    return !method_->client_streaming() && !method_->server_streaming();
  }

  /*!
   * 客户端缓冲流
   * @return true 客户端缓冲流
   * @return false 非客户端缓冲流
   */
  bool IsClientStreaming() const override {
    return method_->client_streaming();
  }

  /*!
   * 服务器缓冲流
   * @return true 服务器缓冲流
   * @return false 非服务器缓冲流
   */
  bool IsServerStreaming() const override {
    return method_->server_streaming();
  }

  /*!
   * 双向缓冲流
   * @return true 双向缓冲流
   * @return false 非双向缓冲流
   */
  bool IsBidiStreaming() const override {
    return method_->client_streaming() && method_->server_streaming();
  }

 private:
  const pb::MethodDescriptor *method_{nullptr}; /*!< protobuf方法 */
};

/*!
 * protobuf 服务
 */
class ProtoBufService
    : public ServiceInterface {
 public:
  ProtoBufService(const pb::ServiceDescriptor *service)
      : service_(service) {}

  /*!
   * 获取前导注释
   * @param prefix 前缀
   * @return 前导注释
   */
  std::string GetLeadingComments(const std::string prefix) const override {
    return GetCommentsHelper(service_, true, prefix);
  }

  /*!
   * 获取尾部注释
   * @param prefix 前缀
   * @return 尾部注释
   */
  std::string GetTrailingComments(const std::string prefix) const override {
    return GetCommentsHelper(service_, false, prefix);
  }

  /*!
   * 获取服务名
   * @return 服务名
   */
  std::string GetName() const override {
    return service_->name();
  };

  /*!
   * 获取方法数量
   * @return 方法数量
   */
  int GetMethodCount() const override {
    return service_->method_count();
  };

  /*!
   * 获取方法
   * @param index 函数位置
   * @return 方法
   */
  std::unique_ptr<const MethodInterface> GetMethodByIndex(int index) const override {
    return std::make_unique<const ProtoBufMethod>(service_->method(index));
  };

 private:
  const pb::ServiceDescriptor *service_{nullptr}; /*!< 服务 */
};

/*!
 * protobuf 打印器
 */
class ProtoBufPrinter
    : public Printer {
 public:
  ProtoBufPrinter(std::string &str)
      : output_stream_(&str), printer_(&output_stream_, '$') {}

  /*!
   *
   * @param vars
   * @param template_string
   */
  void Print(const std::map<std::string, std::string> &vars,
             const char *template_string) override {
    printer_.Print(vars, template_string);
  };

  /*!
   *
   * @param str
   */
  void Print(const char *str) override {
    printer_.Print(str);
  };

  /*!
   *
   * @param str
   */
  void PrintRaw(const char *str) override {
    printer_.PrintRaw(str);
  };

  /*!
   * 缩进
   */
  void Indent() override {
    printer_.Indent();
  };

  /*!
   * 突出
   */
  void Outdent() override {
    printer_.Outdent();
  };

 private:
  pb::io::StringOutputStream output_stream_; /*!< protobuf 输出流 */
  pb::io::Printer printer_; /*!< protobuf 打印器 */
};

/*!
 * protobuf文件
 */
class ProtoBufFile
    : public FileInterface {
 public:
  ProtoBufFile(const pb::FileDescriptor *file)
      : file_(file) {}

  /*!
   * 获取前导注释
   * @param prefix 前缀
   * @return 前导注释
   */
  std::string GetLeadingComments(const std::string prefix) const override {
    return GetCommentsHelper(file_, true, prefix);
  }

  /*!
   * 获取尾部注释
   * @param prefix 前缀
   * @return 尾部注释
   */
  std::string GetTrailingComments(const std::string prefix) const override {
    return GetCommentsHelper(file_, false, prefix);
  }

  /*!
   * 获取文件名
   * @return 文件名
   */
  std::string GetFileName() const override {
    return file_->name();
  }

  /*!
   * 获取文件名不带扩展
   * @return 文件名不带扩展
   */
  std::string GetFileNameWithoutExt() const override {
    return StripProto(file_->name());
  }

  /*!
   * 获取包名
   * @return 包名
   */
  std::string GetPackage() const override {
    return file_->package();
  }

  /*!
   * 获取包名部分
   * @return 包名部分
   */
  std::vector<std::string> GetPackageParts() const override {
    return Tokenize(file_->package(), ".");
  }

  /*!
   *
   * @return
   */
  std::string AdditionalHeaders() const override {
    return "";
  }

  /*!
   * 获取服务数量
   * @return 服务数量
   */
  int GetServiceCount() const override {
    return file_->service_count();
  }

  /*!
   * 获取服务器
   * @param index
   * @return 服务器
   */
  std::unique_ptr<const ServiceInterface> GetServiceByIndex(int index) const override {
    return std::make_unique<const ProtoBufService>(file_->service(index));
  }

  /*!
   * 创建打印器
   * @param str
   * @return 打印器
   */
  std::unique_ptr<Printer> CreatePrinter(std::string &str) const override {
    return std::make_unique<ProtoBufPrinter>(str);
  }

 private:
  const pb::FileDescriptor *file_{nullptr}; /*!< protobuf 描述文件 */
};

/*!
 * rpc文件生成器
 */
class RpcGenerator
    : public pbc::CodeGenerator {
 public:
  RpcGenerator() {}
  ~RpcGenerator() {}

  /*!
   * 生成
   * @param file 文件
   * @param parameter 参数
   * @param generator_context protobuf上下文
   * @param [out] error 错误
   * @return
   */
  bool Generate(const pb::FileDescriptor *file,
                const std::string &parameter,
                pbc::GeneratorContext *generator_context,
                std::string *error) const override;

 private:
  /*!
   * 插入
   * @param context protobuf 上下文
   * @param filename 文件名
   * @param insertion_point 插入点
   * @param code 插入代码
   */
  void Insert(pbc::GeneratorContext *context,
              const std::string &filename,
              const std::string &insertion_point,
              const std::string &code) const;
};

} //! namespace pbrpc
} //! namespace asfinger

#endif //ASFINGER_PBRPC_CORE_RPC_PLUGIN_H_